Pyqt5 での KeyEvent の正しい処理、KeyPressEvent のキャッチに関する問題 (Correct handling of KeyEvent in Pyqt5, problem with catching KeyPressEvent)


問題の説明

Pyqt5 での KeyEvent の正しい処理、KeyPressEvent のキャッチに関する問題 (Correct handling of KeyEvent in Pyqt5, problem with catching KeyPressEvent)

KeyEvent(self, event) 関数があり、event.key() をモジュール MoveKeyboard の関数に転送しますが、 しかキャッチしません>KeyReleaseEvent. ボタンのリリースと継続的な保持を処理し、適切なアクションを実行する必要があります。KeyEvent(self, event) をテストしましたが、コンソール出力は 1: Release のみです。

Joy、rviz、...、move_keyboard には PyQt5.QtWidgets.QWidget クラスがあります。

メイン ウィンドウ コード:

class MainWindow(PyQt5.QtWidgets.QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__(None)
        self.title = 'Robot teleoperation'
        self.left = 10
        self.top = 10
        self.width = 1920
        self.height = 1080
        rospy.init_node("gui_node")

        #self.joy = Joystick(maxDistance=50,MinimumSize=100,EclipseX=‑20,EclipseY=40)
        #self.rviz = Rviz()
        #self.arm_position = BaseArmPosition()
        #self.laser_position = LaserPosition()
        #self.move_slider = MoveSlider()
        #self.arm_slider = ArmSlider()
        self.move_keyboard = MoveKeyboard()

        self.initUI()


    def initUI(self):
        self.central_widget = PyQt5.QtWidgets.QWidget()
        self.setCentralWidget(self.central_widget)

        grid = PyQt5.QtWidgets.QGridLayout(self.centralWidget())

        #grid.addWidget(self.rviz, 0, 0)
        #grid.addWidget(self.joy, 0, 1)
        #grid.addWidget(self.arm_slider,0,2)

        #grid.addWidget(self.arm_position, 1, 0)
        #grid.addWidget(self.move_slider, 1,1)
        grid.addWidget(self.move_keyboard,0,0)

        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)
        self.show() 

    def keyPressEvent(self, event):
        print("1: Press")
        self.move_keyboard.PressEvent(event)
        if event.key() == PyQt5.QtCore.Qt.Key_Control:
            print("2: Press")
            self.move_keyboard.PressEvent(event)
        return super(MainWindow, self).keyPressEvent(event)

    def keyReleaseEvent(self, event):
        print("1: Release")
        self.move_keyboard.ReleaseEvent(event)
        if event.key() == PyQt5.QtCore.Qt.Key_Control:
            print("2: Release")
            self.move_keyboard.ReleaseEvent(event)
        return super(MainWindow, self).keyReleaseEvent(event)

クラス MoveKeyoard:

import PyQt5
import rospy
from geometry_msgs.msg import Twist


class MoveKeyboard(PyQt5.QtWidgets.QWidget):
    def __init__(self, *args, **kwargs):
        super(MoveKeyboard, self).__init__(*args, **kwargs)            
        self.Up = PyQt5.QtWidgets.QLabel("Up(W)")
        self.Left = PyQt5.QtWidgets.QLabel("Left(A)")
        self.Right = PyQt5.QtWidgets.QLabel("Right(D)")
        self.Down = PyQt5.QtWidgets.QLabel("Down(S)")

        self.LinearValue = PyQt5.QtWidgets.QDoubleSpinBox(self)
        self.AngularValue = PyQt5.QtWidgets.QDoubleSpinBox(self)

        self.label_linear = PyQt5.QtWidgets.QLabel("Linear speed")
        self.label_angular = PyQt5.QtWidgets.QLabel("Angular speed")

        self.initUI()


    def initUI(self):
        self.LinearValue.setMaximum(1.00)
        self.LinearValue.setMinimum(0.00)
        self.LinearValue.setSingleStep(0.01)

        self.AngularValue.setMaximum(1.00)
        self.AngularValue.setMinimum(0.00)
        self.AngularValue.setSingleStep(0.01)

        layout = PyQt5.QtWidgets.QGridLayout(self)

        layout.addWidget(self.label_linear,0,0)
        layout.addWidget(self.LinearValue,0,1)
        layout.addWidget(self.label_angular,1,0)
        layout.addWidget(self.AngularValue,1,1)

        layout.addWidget(self.Up,2,1)

        layout.addWidget(self.Left,3,0)
        layout.addWidget(self.Down,3,1)
        layout.addWidget(self.Right,3,2)


        self.pub = rospy.Publisher('cmd_vel', Twist, queue_size = 1)

    def PressEvent(self, e):
        if e.key() == PyQt5.QtCore.Qt.Key_W:
            self.Up.setStyleSheet('color: red')
            self.do("W")
        if e.key() == PyQt5.QtCore.Qt.Key_S:
            self.Down.setStyleSheet('color: red')
            self.do("S")
        if e.key() == PyQt5.QtCore.Qt.Key_A:
            self.Left.setStyleSheet('color: red')
            self.do("A")
        if e.key() == PyQt5.QtCore.Qt.Key_D:
            self.Right.setStyleSheet('color: red')
            self.do("D")

    def ReleaseEvent(self, e):
        if e.key() == PyQt5.QtCore.Qt.Key_W:
            self.Up.setStyleSheet('color: black')
            self.do("‑‑‑W")
        if e.key() == PyQt5.QtCore.Qt.Key_S:
            self.Down.setStyleSheet('color: black')
            self.do("‑‑‑S")
        if e.key() == PyQt5.QtCore.Qt.Key_A:
            self.Left.setStyleSheet('color: black')
            self.do("‑‑‑A")
        if e.key() == PyQt5.QtCore.Qt.Key_D:
            self.Right.setStyleSheet('color: black')
            self.do("‑‑‑D")

    def do(self,str_):
        print(str_)

リファレンスソリューション

方法 1:

If a widget consumes the QKeyEvent event (event.accept()) then that event will not be propagated, in this case QDoubleSpinBox consumes the events they use as a Qt.ControlModifier modifier so that other widgets will not receive the press, only the release.

So the solution in this case is to prevent the QDoubleSpinBox from consuming the combination Ctrl+W, Ctrl+S, Ctrl+A and Ctrl+D.

On the other hand I do not need the window to handle these events, I think it is better for the widget itself to handle them.

from PyQt5 import QtCore, QtWidgets


class DoubleSpinBox(QtWidgets.QDoubleSpinBox):
    def keyPressEvent(self, event):
        if event.modifiers() == QtCore.Qt.ControlModifier and event.key() in (
            QtCore.Qt.Key_W,
            QtCore.Qt.Key_S,
            QtCore.Qt.Key_A,
            QtCore.Qt.Key_D,
        ):
            event.ignore()
        else:
            super(DoubleSpinBox, self).keyPressEvent(event)


class MoveKeyboard(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(MoveKeyboard, self).__init__(parent)
        self.Up = QtWidgets.QLabel("Up(W)")
        self.Left = QtWidgets.QLabel("Left(A)")
        self.Right = QtWidgets.QLabel("Right(D)")
        self.Down = QtWidgets.QLabel("Down(S)")

        self.LinearValue = DoubleSpinBox()
        self.AngularValue = DoubleSpinBox()

        self.label_linear = QtWidgets.QLabel("Linear speed")
        self.label_angular = QtWidgets.QLabel("Angular speed")

        self.initUI()

    def initUI(self):
        self.LinearValue.setMaximum(1.00)
        self.LinearValue.setMinimum(0.00)
        self.LinearValue.setSingleStep(0.01)

        self.AngularValue.setMaximum(1.00)
        self.AngularValue.setMinimum(0.00)
        self.AngularValue.setSingleStep(0.01)

        layout = QtWidgets.QGridLayout(self)

        layout.addWidget(self.label_linear, 0, 0)
        layout.addWidget(self.LinearValue, 0, 1)
        layout.addWidget(self.label_angular, 1, 0)
        layout.addWidget(self.AngularValue, 1, 1)

        layout.addWidget(self.Up, 2, 1)

        layout.addWidget(self.Left, 3, 0)
        layout.addWidget(self.Down, 3, 1)
        layout.addWidget(self.Right, 3, 2)

    def get_widget_by_key(self, key):
        d = {
            QtCore.Qt.Key_W: ("W", self.Up),
            QtCore.Qt.Key_S: ("S", self.Down),
            QtCore.Qt.Key_A: ("A", self.Left),
            QtCore.Qt.Key_D: ("D", self.Right),
        }
        return d.get(key, ("", None))

    def keyPressEvent(self, event):
        if event.modifiers() == QtCore.Qt.ControlModifier and not event.isAutoRepeat():
            letter, widget = self.get_widget_by_key(event.key())
            if widget is not None:
                widget.setStyleSheet("color: red")
                print(letter)
        super(MoveKeyboard, self).keyPressEvent(event)

    def keyReleaseEvent(self, event):
        if event.modifiers() == QtCore.Qt.ControlModifier and not event.isAutoRepeat():
            letter, widget = self.get_widget_by_key(event.key())
            if widget is not None:
                widget.setStyleSheet("color: black")
                print("‑‑{}".format(letter))
        super(MoveKeyboard, self).keyReleaseEvent(event)


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__(None)
        self.title = "Robot teleoperation"
        self.move_keyboard = MoveKeyboard()
        self.initUI()

    def initUI(self):
        self.central_widget = QtWidgets.QWidget()
        self.setCentralWidget(self.central_widget)
        grid = QtWidgets.QGridLayout(self.centralWidget())
        grid.addWidget(self.move_keyboard, 0, 0)
        self.setWindowTitle(self.title)
        self.setGeometry(10, 10, 1920, 1080)


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec_())

(by Lukaeyllanesc)

リファレンスドキュメント

  1. Correct handling of KeyEvent in Pyqt5, problem with catching KeyPressEvent (CC BY‑SA 2.5/3.0/4.0)

#Python #keyboard-events #pyqt5






関連する質問

再帰的なテキスト分割の問題 (Trouble with recursive text splitting)

行継続文字エラーの後に予期しない文字が表示されます (I am getting an unexpected character after line continuation character error)

distutils で Tkinter を要求するにはどうすればよいですか? (How do I require Tkinter with distutils?)

Python の super() と super (className,self) の違い (Difference between super() and super (className,self) in Python)

TensorFlow 2はcolab googleとwindows 10でバージョンを表示しません (TensorFlow 2 not show the version in colab google and windows 10)

それぞれが親ループに依存する4つのネストされたループの時間の複雑さは何ですか? (What is time complexity of 4 nested loops which each depend on the parent loop)

Pyqt5 での KeyEvent の正しい処理、KeyPressEvent のキャッチに関する問題 (Correct handling of KeyEvent in Pyqt5, problem with catching KeyPressEvent)

文字列形式の辞書のリストを Python のデータフレームに変換する方法はありますか? (Is there a way to convert list of string formatted dictionary to a dataframe in Python?)

母音 + 周囲の子音で文字列を分割 (split string at vowel + surrounding consonants)

plumbum: stdin に変数を送信する方法は? (plumbum: How to send a variable to stdin?)

Python - ビデオ処理。方法: 1) ビデオのピクセル値を変更し (つまり、ピクセル効果)、2) すべてのピクセルの情報を取得します。 (Python - video processing. How to: 1) change pixels value in videos (ie pixelated effect), and then 2) retrieve every single pixel's information)

ボットが 1 つのコマンド discord.py に複数回応答する問題 (Issue with bot responding multiple times to one command discord.py)







コメント